#include "stdafx.h"
#include "SkiaHost.h"
#include "FCFile.h"

Uint32 timeCallback(Uint32 interval, void *param){
    SkiaHost *host = (SkiaHost*)param;
    host->onTimer();
    return interval;
}

SkiaTimer::SkiaTimer(){
    m_interval = 1000;
    m_tick = 0;
    m_timerID = 0;
}

SkiaTimer::~SkiaTimer(){

}

SkiaHost::SkiaHost(){
    m_native = 0;
    m_allowOperate = true;
    m_allowPartialPaint = false;
    m_touchPoint.x = 0;
    m_touchPoint.y = 0;
    m_clientSize.cx = 0;
    m_clientSize.cy = 0;
    m_invokeSerialID = 0;
    m_window = 0;
}

SkiaHost::~SkiaHost(){
    std::map<int, SkiaTimer*>::iterator sIter = m_timers.begin();
    for(; sIter != m_timers.end(); ++sIter){
        delete sIter->second;
    }
    m_timers.clear();
    m_native = 0;
    m_window = 0;
}

FCNative* SkiaHost::getNative(){
    return m_native;
}

void SkiaHost::setNative(FCNative *value){
    m_native = value;
}

FCSize SkiaHost::getClientSize(){
    return m_clientSize;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void SkiaHost::activeMirror(){
}

bool SkiaHost::allowOperate(){
    return m_allowOperate;
}

bool SkiaHost::allowPartialPaint(){
    return m_allowPartialPaint;
}

void SkiaHost::beginInvoke(FCView *view, Object args){
    int seraialID = 0;
    m_lock.lock();
    m_invokeSerialID++;
    seraialID = m_invokeSerialID;
    m_invokeArgs[seraialID] = args;
    m_invokeViews[seraialID] = view;
    m_lock.unLock();
}

void SkiaHost::copy(lstring text){
}

FCView* SkiaHost::createInternalView(FCView *parent, const String& clsid){
    FCSplitLayoutDiv *splitLayoutDiv = dynamic_cast<FCSplitLayoutDiv*>(parent);
    if (splitLayoutDiv){
        if (clsid == L"splitter"){
            FCButton *splitter = new FCButton;
            FCSize size = {5, 5};
            splitter->setSize(size);
            return splitter;
        }
    }
    FCScrollBar *scrollBar = dynamic_cast<FCScrollBar*>(parent);
    if (scrollBar){
        if (clsid == L"addbutton"){
            FCButton *addButton = new FCButton;
            FCSize size = {15, 15};
            addButton->setSize(size);
            return addButton;
        }
        else if (clsid == L"backbutton"){
            return new FCButton();
        }
        else if (clsid == L"scrollbutton"){
            FCButton *scrollButton = new FCButton;
            scrollButton->setAllowDrag(true);
            return scrollButton;
        }
        else if (clsid == L"reducebutton"){
            FCButton *reduceButton = new FCButton;
            FCSize size = {15, 15};
            reduceButton->setSize(size);
            return reduceButton;
        }
    }
    FCTabPage *tabPage = dynamic_cast<FCTabPage*>(parent);
    if (tabPage){
        if (clsid == L"headerbutton"){
            FCButton *button = new FCButton;
            button->setAllowDrag(true);
            FCSize size = {100, 20};
            button->setSize(size);
            return button;
        }
    }
    FCComboBox *comboBox = dynamic_cast<FCComboBox*>(parent);
    if (comboBox){
        if (clsid == L"dropdownbutton"){
            FCButton *dropDownButton = new FCButton;
            dropDownButton->setDisplayOffset(false);
            int width = comboBox->getWidth();
            int height = comboBox->getHeight();
            FCPoint location = {width - 16, 0};
            dropDownButton->setLocation(location);
            FCSize size = {16, height};
            dropDownButton->setSize(size);
            return dropDownButton;
        }
        else if (clsid == L"dropdownmenu"){
            FCComboBoxMenu *comboBoxMenu = new FCComboBoxMenu;
            comboBoxMenu->setComboBox(comboBox);
            comboBoxMenu->setPopup(true);
            FCSize size = {100, 200};
            comboBoxMenu->setSize(size);
            return comboBoxMenu;
        }
    }
    FCDateTimePicker *datePicker = dynamic_cast<FCDateTimePicker*>(parent);
    if (datePicker){
        if (clsid == L"dropdownbutton"){
            FCButton *dropDownButton = new FCButton;
            dropDownButton->setDisplayOffset(false);
            int width = datePicker->getWidth();
            int height = datePicker->getHeight();
            FCPoint location = {width - 16, 0};
            dropDownButton->setLocation(location);
            FCSize size = {16, height};
            dropDownButton->setSize(size);
            return dropDownButton;
        }
        else if (clsid == L"dropdownmenu"){
            FCMenu *dropDownMenu = new FCMenu();
            FCPadding padding(1);
            dropDownMenu->setPadding(padding);
            dropDownMenu->setPopup(true);
            FCSize size = {200, 200};
            dropDownMenu->setSize(size);
            return dropDownMenu;
        }
    }
    FCSpin *spin = dynamic_cast<FCSpin*>(parent);
    if (spin){
        if (clsid == L"downbutton"){
            FCButton *downButton = new FCButton;
            downButton->setText(L"-");
            downButton->setDisplayOffset(false);
            FCSize size = {16, 16};
            downButton->setSize(size);
            return downButton;
        }
        else if (clsid == L"upbutton"){
            FCButton *upButton = new FCButton;
            upButton->setText(L"+");
            upButton->setDisplayOffset(false);
            FCSize size = {16, 16};
            upButton->setSize(size);
            return upButton;
        }
    }
    FCDiv *div = dynamic_cast<FCDiv*>(parent);
    if (div){
        if (clsid == L"hscrollbar"){
            FCHScrollBar *hScrollBar = new FCHScrollBar;
            hScrollBar->setVisible(false);
            FCSize size = {15, 15};
            hScrollBar->setSize(size);
            return hScrollBar;
        }
        else if (clsid == L"vscrollbar"){
            FCVScrollBar *vScrollBar = new FCVScrollBar;
            vScrollBar->setVisible(false);
            FCSize size = {15, 15};
            vScrollBar->setSize(size);
            return vScrollBar;
        }
    }
    FCGrid *grid = dynamic_cast<FCGrid*>(parent);
    if(grid){
        if(clsid == L"edittextbox"){
            return new FCTextBox;
        }
    }
    return 0;
}

FCCursors SkiaHost::getCursor(){
    return FCCursors_Arrow;
}

int SkiaHost::getIntersectRect(FCRect *lpDestRect, const FCRect *lpSrc1Rect, const FCRect *lpSrc2Rect){
    lpDestRect->left = FCTran::maxValue(lpSrc1Rect->left, lpSrc2Rect->left);
    lpDestRect->right = FCTran::minValue(lpSrc1Rect->right, lpSrc2Rect->right);
    lpDestRect->top = FCTran::maxValue(lpSrc1Rect->top, lpSrc2Rect->top);
    lpDestRect->bottom = FCTran::minValue(lpSrc1Rect->bottom, lpSrc2Rect->bottom);
    if(lpDestRect->right > lpDestRect->left && lpDestRect->bottom > lpDestRect->top){
        return 1;
    }
    else{
        lpDestRect->left = 0;
        lpDestRect->right = 0;
        lpDestRect->top = 0;
        lpDestRect->bottom = 0;
        return 0;
    }
}

FCPoint SkiaHost::getTouchPoint(){
    FCPoint mp = m_touchPoint;
    if (m_native->allowScaleSize())
    {
        FCSize clientSize = getClientSize();
        if (clientSize.cx > 0 && clientSize.cy > 0)
        {
            FCSize scaleSize = m_native->getScaleSize();
            mp.x = mp.x * scaleSize.cx / clientSize.cx;
            mp.y = mp.y * scaleSize.cy / clientSize.cy;
        }
    }
    return mp;
}

FCSize SkiaHost::getSize(){
    if (m_native->allowScaleSize()){
        return m_native->getScaleSize();
    }
    else{
        return getClientSize();
    }
}

int SkiaHost::getUnionRect(FCRect *lpDestRect, const FCRect *lpSrc1Rect, const FCRect *lpSrc2Rect){
    return 0;
}

void SkiaHost::invalidate(){
    m_clipBounds.clear();
    FCRect clipRect = {0, 0, m_native->getSize().cx, m_native->getSize().cy};
    m_clipBounds.add(clipRect);
    onPaint(clipRect);
}

void SkiaHost::invalidate(const FCRect& rect){
    if(m_allowPartialPaint){
        FCSize displaySize = m_native->getSize();
        double scaleFactorX = 1, scaleFactorY = 1;
        FCSize clientSize = getClientSize();
        if (m_native->allowScaleSize()){
            if (clientSize.cx > 0 && clientSize.cy > 0){
                FCSize scaleSize = m_native->getScaleSize();
                scaleFactorX = (double)(clientSize.cx) / scaleSize.cx;
                scaleFactorY = (double)(clientSize.cy) / scaleSize.cy;
            }
        }
        FCRect newRect = rect;
        if(scaleFactorX > 0 && scaleFactorY > 0){
            newRect.left = floor(newRect.left * scaleFactorX);
            newRect.top = floor(newRect.top * scaleFactorY);
            newRect.right = ceil(newRect.right * scaleFactorX);
            newRect.bottom = ceil(newRect.bottom * scaleFactorY);
        }
        m_clipBounds.add({rect.left >= 0 ? rect.left : 0, rect.top >= 0 ? rect.top : 0, rect.right <= displaySize.cx ? rect
                .right : displaySize.cx, rect.bottom <= displaySize.cy ? rect.bottom : displaySize.cy});
        onPaint(rect);
    }
    else{
        invalidate();
    }
}

void SkiaHost::invoke(FCView *view, Object args){
    int seraialID = 0;
    m_lock.lock();
    m_invokeSerialID++;
    seraialID = m_invokeSerialID;
    m_invokeArgs[seraialID] = args;
    m_invokeViews[seraialID] = view;
    m_lock.unLock();
}

bool SkiaHost::isKeyPress(char key){
    SDL_Keymod keyMod = SDL_GetModState();
    if(key == VK_CONTROL){
        if(keyMod & KMOD_CTRL){
            return true;
        }
    }else if(key == VK_SHIFT){
        if(keyMod & KMOD_SHIFT){
            return true;
        }
    }else if(key == VK_MENU){
        if(keyMod & KMOD_ALT){
            return true;
        }
    }
    return false;
}

void SkiaHost::onPaint(const FCRect& rect){
    FCSize displaySize = m_native->getSize();
    double scaleFactorX = 1, scaleFactorY = 1;
    FCSize clientSize = getClientSize();
    if (m_native->allowScaleSize()){
        if (clientSize.cx > 0 && clientSize.cy > 0){
            FCSize scaleSize = m_native->getScaleSize();
            scaleFactorX = (double)(clientSize.cx) / scaleSize.cx;
            scaleFactorY = (double)(clientSize.cy) / scaleSize.cy;
        }
    }
    FCPaint *paint = m_native->getPaint();
    FCRect wRect = {0, 0, clientSize.cx, clientSize.cy};
    paint->setScaleFactor(scaleFactorX, scaleFactorY);
    FCRect clipRect = {0};
    int clipBoundsSize = m_clipBounds.size();
    clipRect = {0};
    if (clipBoundsSize > 0) {
        for (int i = 0; i < clipBoundsSize; i++) {
            FCRect clipBounds = m_clipBounds.get(i);
            if (i == 0) {
                clipRect = {clipBounds.left, clipBounds.top, clipBounds.right, clipBounds.bottom};
            } else {
                if (clipRect.left > clipBounds.left) {
                    clipRect.left = clipBounds.left;
                }
                if (clipRect.right < clipBounds.right) {
                    clipRect.right = clipBounds.right;
                }
                if (clipRect.top > clipBounds.top) {
                    clipRect.top = clipBounds.top;
                }
                if (clipRect.bottom < clipBounds.bottom) {
                    clipRect.bottom = clipBounds.bottom;
                }
            }
        }
    } else {
        clipRect = {0, 0, displaySize.cx, displaySize.cy};
    }
    m_clipBounds.clear();
    if(wRect.right - wRect.left > 0 && wRect.bottom - wRect.top > 0){
        paint->beginPaint(0, wRect, clipRect);
        m_native->onPaint(clipRect);
        paint->endPaint();
    }
}

void SkiaHost::onMessage(){
    SDL_TimerID timerID = SDL_AddTimer(10, timeCallback, this);
    bool quit = false;
	SDL_Event event;
	while (!quit) {
		while (SDL_PollEvent(&event)) {
			if (event.type == SDL_QUIT) {
				quit = true;
            }else if(event.type == SDL_WINDOWEVENT){
                if(event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED){
                    int w = 0, h = 0;
                    SDL_GetWindowSize(m_window, &w, &h);
                    FCSize newSize = {w, h};
                    m_clientSize = newSize;
                    m_native->setSize(newSize);
                    m_native->update();
                    m_native->invalidate();
                }
			}else if(event.type == SDL_TEXTINPUT){
				lstring inputText = event.text.text;
				if((int)inputText.length() > 0){
					FCView *focusedView = m_native->getFocusedView();
					if(focusedView){
						FCTextBox *textBox = dynamic_cast<FCTextBox*>(focusedView);
						if(textBox && !textBox->m_readOnly){
							if(!isKeyPress(VK_CONTROL)){
								int oldLines = (int)textBox->m_lines.size();
								FCRedoUndoInfo newRedoUndoInfo(textBox->getText(), textBox->getSelectionStart(),
								textBox->getSelectionLength());
								String str = FCTran::stringToString(inputText);
								textBox->insertWord(str);
								textBox->onTextChanged();
								if(textBox->m_textChanged){
									textBox->m_undoStack.push(newRedoUndoInfo);
								}
								textBox->invalidate();
								int newLines = (int)textBox->m_lines.size();
								if(newLines != oldLines){
									FCVScrollBar *vScrollBar = textBox->getVScrollBar();
									if(vScrollBar){
										vScrollBar->setPos(vScrollBar->getPos() + textBox->m_lineHeight * (newLines - oldLines));
										textBox->invalidate();
									}
								}
							}
						}
					}
				}
			}else if(event.type == SDL_MOUSEBUTTONDOWN){
				 if (event.button.button == SDL_BUTTON_LEFT) {
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = true;
					touchInfo.m_secondTouch = false;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 1;
					m_touchPoint = mp;
					FCView *lastFocusedView = m_native->getFocusedView();
					m_native->onMouseDown(touchInfo); 
					FCView *newFocusedView = m_native->getFocusedView();
					if(newFocusedView != lastFocusedView){
						if(lastFocusedView && dynamic_cast<FCTextBox*>(lastFocusedView)){
							SDL_StopTextInput();
						}
						if(newFocusedView && dynamic_cast<FCTextBox*>(newFocusedView)){
							SDL_StartTextInput();
							FCPaint *paint = m_native->getPaint();
							double scaleFactorX = 0, scaleFactorY = 0;
							paint->getScaleFactor(&scaleFactorX, &scaleFactorY);
							FCTextBox *textBox = dynamic_cast<FCTextBox*>(newFocusedView);
							SDL_Rect inputRect;
							inputRect.x = (m_native->clientX(newFocusedView) + textBox->m_cursorPoint.x) / scaleFactorX;
							inputRect.y = (m_native->clientY(newFocusedView) + textBox->getLineHeight() / 2) / scaleFactorY;
							inputRect.w = 1;
							inputRect.h = textBox->getLineHeight() / scaleFactorY;
							SDL_SetTextInputRect(&inputRect);
						}
					}
				} else if (event.button.button == SDL_BUTTON_RIGHT) {
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = false;
					touchInfo.m_secondTouch = true;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 1;
					m_touchPoint = mp;
					FCView *lastFocusedView = m_native->getFocusedView();
					m_native->onMouseDown(touchInfo); 
					FCView *newFocusedView = m_native->getFocusedView();
					if(newFocusedView != lastFocusedView){
						if(lastFocusedView && dynamic_cast<FCTextBox*>(lastFocusedView)){
							SDL_StopTextInput();
						}
						if(newFocusedView && dynamic_cast<FCTextBox*>(newFocusedView)){
							SDL_StartTextInput();
							FCPaint *paint = m_native->getPaint();
							double scaleFactorX = 0, scaleFactorY = 0;
							paint->getScaleFactor(&scaleFactorX, &scaleFactorY);

							FCTextBox *textBox = dynamic_cast<FCTextBox*>(newFocusedView);
							SDL_Rect inputRect;
							inputRect.x = (m_native->clientX(newFocusedView) + textBox->m_cursorPoint.x) / scaleFactorX;
							inputRect.y = (m_native->clientY(newFocusedView) + textBox->getLineHeight() / 2) / scaleFactorY;
							inputRect.w = 1;
							inputRect.h = textBox->getLineHeight() / scaleFactorY;
							SDL_SetTextInputRect(&inputRect);
						}
					}
				}

			}else if(event.type == SDL_MOUSEBUTTONUP){
				 if (event.button.button == SDL_BUTTON_LEFT) {
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = true;
					touchInfo.m_secondTouch = false;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 1;
					m_touchPoint = mp;
					m_native->onMouseUp(touchInfo); 
				} else if (event.button.button == SDL_BUTTON_RIGHT) {
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = false;
					touchInfo.m_secondTouch = true;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 1;
					m_touchPoint = mp;
					m_native->onMouseUp(touchInfo); 
				}

			}else if(event.type == SDL_MOUSEMOTION){
				 if (event.button.button == SDL_BUTTON_LEFT) {
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = true;
					touchInfo.m_secondTouch = false;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 1;
					m_touchPoint = mp;
					m_native->onMouseMove(touchInfo); 
				} else if (event.button.button == SDL_BUTTON_RIGHT) {
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = false;
					touchInfo.m_secondTouch = true;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 1;
					m_touchPoint = mp;
					m_native->onMouseMove(touchInfo); 
				}else{
					int x = event.button.x;
					int y = event.button.y;
					FCTouchInfo touchInfo;
					touchInfo.m_firstTouch = false;
					touchInfo.m_secondTouch = false;
					FCPoint mp = {x, y};
					touchInfo.m_firstPoint = mp;
					touchInfo.m_secondPoint = mp;
					touchInfo.m_clicks = 0;
					m_touchPoint = mp;
					m_native->onMouseMove(touchInfo); 
				}
			}else if(event.type == SDL_MOUSEWHEEL){
				int x = event.button.x;
				int y = event.button.y;
				FCTouchInfo touchInfo;
				touchInfo.m_firstTouch = false;
				touchInfo.m_secondTouch = false;
				FCPoint mp = {x, y};
				touchInfo.m_firstPoint = mp;
				touchInfo.m_secondPoint = mp;
				touchInfo.m_clicks = 0;
				touchInfo.m_delta = event.wheel.y;
				m_native->onMouseWheel(touchInfo); 
			}else if(event.type == SDL_KEYDOWN){
				switch(event.key.keysym.sym){
					case SDLK_UP:
						m_native->onKeyDown(38);
						break;
					case SDLK_DOWN:
						m_native->onKeyDown(40);
						break;
					case SDLK_LEFT:
						m_native->onKeyDown(37);
						break;
					case SDLK_RIGHT:
						m_native->onKeyDown(39);
						break;
					default:
						//native->onChar(event.key.keysym.sym);
						m_native->onKeyDown(event.key.keysym.sym);
						break;
				}
			}else if(event.type == SDL_KEYUP){
				switch(event.key.keysym.sym){
					case SDLK_UP:
						m_native->onKeyUp(38);
						break;
					case SDLK_DOWN:
						m_native->onKeyUp(40);
						break;
					case SDLK_LEFT:
						m_native->onKeyUp(37);
						break;
					case SDLK_RIGHT:
						m_native->onKeyUp(39);
						break;
					default:
						m_native->onKeyUp(event.key.keysym.sym);
						break;
				}
			}
		}
	}
    SDL_RemoveTimer(timerID);
}

void SkiaHost::onTimer(){
    if(m_native){
        ArrayList<int> timerIDs;
        std::map<int, SkiaTimer*>::iterator sIter = m_timers.begin();
        for(; sIter != m_timers.end(); ++sIter){
            SkiaTimer *timer = sIter->second;
            if (timer->m_tick % timer->m_interval == 0){
                timerIDs.add(timer->m_timerID);

            }
            timer->m_tick++;
        }
        for(int i = 0; i < timerIDs.size(); i++){
            m_native->onTimer(timerIDs.get(i));
        }
        timerIDs.clear();
        ArrayList<int> sIds;
        m_lock.lock();
        std::map<int, Object>::iterator sIterX = m_invokeArgs.begin();
        for(; sIterX != m_invokeArgs.end(); ++sIterX){
            sIds.add(sIterX->first);
        }
        m_lock.unLock();
        if(sIds.size() > 0) {
            for (int i = 0; i < sIds.size(); i++) {
                Object args = 0;
                FCView *view = 0;
                int invokeSerialID = sIds.get(i);
                m_lock.lock();
                std::map<int, Object>::iterator sIter = m_invokeArgs.find(invokeSerialID);
                if (sIter != m_invokeArgs.end()) {
                    args = sIter->second;
                    m_invokeArgs.erase(sIter);
                }
                std::map<int, FCView *>::iterator sIter2 = m_invokeViews.find(invokeSerialID);
                if (sIter2 != m_invokeViews.end()) {
                    view = sIter2->second;
                    m_invokeViews.erase(sIter2);
                }
                m_lock.unLock();
                if (view) {
                    view->onInvoke(args);
                }
            }
        }
    }
}

lstring SkiaHost::paste(){
    return "";
}

void SkiaHost::setAllowOperate(bool allowOperate){
    m_allowOperate = allowOperate;
}

void SkiaHost::setAllowPartialPaint(bool allowPartialPaint){
    m_allowPartialPaint = allowPartialPaint;
}

void SkiaHost::setCursor(FCCursors cursor){
}

void SkiaHost::showToolTip(const String& text, const FCPoint& mp){
}

void SkiaHost::startTimer(int timerID, int interval){
    interval = interval / 10;
    if(interval < 1){
        interval = 1;
    }
    //m_lock.lock();
    std::map<int, SkiaTimer*>::iterator sIter = m_timers.find(timerID);
    if(sIter != m_timers.end()){
        sIter->second->m_interval = interval;
        sIter->second->m_tick = 0;
    }
    else{
        SkiaTimer *timer = new SkiaTimer;
        timer->m_interval = interval;
        timer->m_timerID = timerID;
        m_timers[timerID] = timer;
    }
}

void SkiaHost::stopTimer(int timerID){
    std::map<int, SkiaTimer*>::iterator sIter = m_timers.find(timerID);
    if(sIter != m_timers.end()){
        delete sIter->second;
        m_timers.erase(sIter);
    }
}
